<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  

  
  <title>Node.js异步机制与事件循环 | My Blog</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="原文：nodejs真的是单线程吗？ 多线程与单线程像java、python这个可以具有多线程的语言。多线程同步模式是这样的，将cpu分成几个线程，每个线程同步运行。  而node.js采用单线程异步非阻塞模式，也就是说每一个计算独占cpu，遇到I&#x2F;O请求不阻塞后面的计算，当I&#x2F;O完成后，以事件的方式通知，继续执行计算2。  事件驱动、异步、单线程、非阻塞I&#x2F;O，这是我们听得最多的关于nodejs">
<meta property="og:type" content="article">
<meta property="og:title" content="Node.js异步机制与事件循环">
<meta property="og:url" content="http://yoursite.com/node-async/index.html">
<meta property="og:site_name" content="My Blog">
<meta property="og:description" content="原文：nodejs真的是单线程吗？ 多线程与单线程像java、python这个可以具有多线程的语言。多线程同步模式是这样的，将cpu分成几个线程，每个线程同步运行。  而node.js采用单线程异步非阻塞模式，也就是说每一个计算独占cpu，遇到I&#x2F;O请求不阻塞后面的计算，当I&#x2F;O完成后，以事件的方式通知，继续执行计算2。  事件驱动、异步、单线程、非阻塞I&#x2F;O，这是我们听得最多的关于nodejs">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNf0?w=489&h=466">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNgD?w=481&h=561">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNhj?w=865&h=374">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNhu?w=865&h=418">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNhM?w=398&h=483">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNh0?w=503&h=493">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNie?w=444&h=461">
<meta property="og:image" content="https://segmentfault.com/img/bVbaNjy?w=779&h=584">
<meta property="article:published_time" content="2020-10-09T16:44:00.000Z">
<meta property="article:modified_time" content="2021-03-04T16:00:51.156Z">
<meta property="article:author" content="Alanisia">
<meta property="article:tag" content="nodejs">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://segmentfault.com/img/bVbaNf0?w=489&h=466">
  
    <link rel="alternate" href="/atom.xml" title="My Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.ico">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  
<link rel="stylesheet" href="/css/style.css">

<meta name="generator" content="Hexo 4.2.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">My Blog</a>
      </h1>
      
        <h2 id="subtitle-wrap">
          <a href="/" id="subtitle">我的博客 -- 记录编程过程中所遇到的问题与总结</a>
        </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="搜索"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main"><article id="post-node-async" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/node-async/" class="article-date">
  <time datetime="2020-10-09T16:44:00.000Z" itemprop="datePublished">2020-10-09</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      Node.js异步机制与事件循环
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p> 原文：<a href="https://www.cnblogs.com/wxmdevelop/p/10234556.html" target="_blank" rel="noopener">nodejs真的是单线程吗？</a></p>
<h2 id="多线程与单线程"><a href="#多线程与单线程" class="headerlink" title="多线程与单线程"></a>多线程与单线程</h2><p>像java、python这个可以具有多线程的语言。多线程同步模式是这样的，将cpu分成几个线程，每个线程同步运行。</p>
<p><img src="https://segmentfault.com/img/bVbaNf0?w=489&h=466" alt="图片描述"></p>
<p>而node.js采用单线程异步非阻塞模式，也就是说每一个计算独占cpu，遇到I/O请求不阻塞后面的计算，当I/O完成后，以事件的方式通知，继续执行计算2。</p>
<p><img src="https://segmentfault.com/img/bVbaNgD?w=481&h=561" alt="图片描述"></p>
<p>事件驱动、异步、单线程、非阻塞I/O，这是我们听得最多的关于nodejs的介绍。看到上面的关键字，可能我们会好奇：</p>
<p><strong>为什么在浏览器中运行的 Javascript 能与操作系统进行如此底层的交互？</strong><br><strong>nodejs既然是单线程，如何实现异步、非阻塞I/O？</strong><br><strong>nodejs全是异步调用和非阻塞I/O，就真的不用管并发数了吗？</strong><br><strong>nodejs事件驱动是如何实现的？和浏览器的event loop是一回事吗？</strong><br><strong>nodejs擅长什么？不擅长什么？</strong></p>
<h2 id="nodejs内部揭秘"><a href="#nodejs内部揭秘" class="headerlink" title="nodejs内部揭秘"></a>nodejs内部揭秘</h2><p>要弄清楚上面的问题，首先要弄清楚nodejs是怎么工作的。</p>
<p><img src="https://segmentfault.com/img/bVbaNhj?w=865&h=374" alt="图片描述"></p>
<p>我们可以看到，Node.js 的结构大致分为三个层次：</p>
<p>1、 Node.js 标准库，这部分是由 Javascript 编写的，即我们使用过程中直接能调用的 API。在源码中的 lib 目录下可以看到。</p>
<p>2、 Node bindings，这一层是 Javascript 与底层 C/C++ 能够沟通的关键，前者通过 bindings 调用后者，相互交换数据。</p>
<p>3、这一层是支撑 Node.js 运行的关键，由 C/C++ 实现。<br>V8：Google 推出的 Javascript VM，也是 Node.js 为什么使用的是 Javascript 的关键，它为 Javascript 提供了在非浏览器端运行的环境，它的高效是 Node.js 之所以高效的原因之一。<br>Libuv：它为 Node.js 提供了跨平台，线程池，事件池，异步 I/O 等能力，是 Node.js 如此强大的关键。<br>C-ares：提供了异步处理 DNS 相关的能力。<br>http_parser、OpenSSL、zlib 等：提供包括 http 解析、SSL、数据压缩等其他的能力。</p>
<h2 id="libuv简介"><a href="#libuv简介" class="headerlink" title="libuv简介"></a>libuv简介</h2><p><img src="https://segmentfault.com/img/bVbaNhu?w=865&h=418" alt="图片描述"></p>
<p>可以看出，几乎所有和操作系统打交道的部分都离不开 libuv的支持。libuv也是node实现跨操作系统的核心所在。</p>
<h2 id="我们再来看看最开始我抛出的问题"><a href="#我们再来看看最开始我抛出的问题" class="headerlink" title="我们再来看看最开始我抛出的问题"></a>我们再来看看最开始我抛出的问题</h2><h3 id="问题一：为什么在浏览器中运行的-Javascript-能与操作系统进行如此底层的交互？"><a href="#问题一：为什么在浏览器中运行的-Javascript-能与操作系统进行如此底层的交互？" class="headerlink" title="问题一：为什么在浏览器中运行的 Javascript 能与操作系统进行如此底层的交互？"></a>问题一：为什么在浏览器中运行的 Javascript 能与操作系统进行如此底层的交互？</h3><p>举个简单的例子，我们想要打开一个文件，并进行一些操作，可以写下面这样一段代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>); </span><br><span class="line">fs.open(<span class="string">'./test.txt'</span>, <span class="string">"w"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">err, fd</span>) </span>&#123;</span><br><span class="line">    <span class="comment">//..do something</span></span><br><span class="line">&#125;);  </span><br><span class="line">fs.open = <span class="function"><span class="keyword">function</span>(<span class="params">path, flags, mode, callback</span>) </span>&#123;      </span><br><span class="line">    <span class="comment">// ...     </span></span><br><span class="line">    binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, callback);  </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这段代码的调用过程大致可描述为：lib/fs.js → src/node_file.cc →uv_fs</p>
<p><img src="https://segmentfault.com/img/bVbaNhM?w=398&h=483" alt="图片描述"></p>
<p>从JavaScript调用Node的核心模块，核心模块调用C++内建模块，内建模块通过 libuv进行系统调用，这是Node里经典的调用方式。总体来说，我们在 Javascript 中调用的方法，最终都会通过node-bindings 传递到 C/C++ 层面，最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。</p>
<h3 id="问题二：nodejs既然是单线程，如何实现异步、非阻塞I-O？"><a href="#问题二：nodejs既然是单线程，如何实现异步、非阻塞I-O？" class="headerlink" title="问题二：nodejs既然是单线程，如何实现异步、非阻塞I/O？"></a>问题二：nodejs既然是单线程，如何实现异步、非阻塞I/O？</h3><p>顺便回答标题nodejs真的是单线程吗？其实只有js执行是单线程，I/O显然是其它线程。<br>js执行线程是单线程，把需要做的I/O交给libuv，自己马上返回做别的事情，然后libuv在指定的时刻回调就行了。其实简化的流程就是酱紫的！细化一点，nodejs会先从js代码通过node-bindings调用到C/C++代码，然后通过C/C++代码封装一个叫 “请求对象” 的东西交给libuv，这个请求对象里面无非就是需要执行的功能+回调之类的东西，给libuv执行以及执行完实现回调。</p>
<p>总结来说，一个异步 I/O 的大致流程如下：</p>
<p>1、发起 I/O 调用<br>用户通过 Javascript 代码调用 Node 核心模块，将参数和回调函数传入到核心模块；<br>Node 核心模块会将传入的参数和回调函数封装成一个请求对象；<br>将这个请求对象推入到 I/O 线程池等待执行；<br>Javascript 发起的异步调用结束，Javascript 线程继续执行后续操作。</p>
<p>2、执行回调<br>I/O 操作完成后，会取出之前封装在请求对象中的回调函数，执行这个回调函数，以完成 Javascript 回调的目的。（这里回调的细节下面讲解）</p>
<p><img src="https://segmentfault.com/img/bVbaNh0?w=503&h=493" alt="图片描述"></p>
<p>从这里，我们可以看到，我们其实对 Node.js 的单线程一直有个误会。事实上，它的单线程指的是自身 Javascript 运行环境的单线程，Node.js 并没有给 Javascript 执行时创建新线程的能力，最终的实际操作，还是通过 Libuv 以及它的事件循环来执行的。这也就是为什么 Javascript 一个单线程的语言，能在 Node.js 里面实现异步操作的原因，两者并不冲突。</p>
<h3 id="问题三：nodejs全是异步调用和非阻塞I-O，就真的不用管并发数了吗？"><a href="#问题三：nodejs全是异步调用和非阻塞I-O，就真的不用管并发数了吗？" class="headerlink" title="问题三：nodejs全是异步调用和非阻塞I/O，就真的不用管并发数了吗？"></a>问题三：nodejs全是异步调用和非阻塞I/O，就真的不用管并发数了吗？</h3><p>之前我们就提到了线程池的概念，发现nodejs并不是单线程的，而且还有并行事件发生。同时，线程池默认大小是 4 ，也就是说，同时能有4个线程去做文件i/o的工作，剩下的请求会被挂起等待直到线程池有空闲。 所以nodejs对于并发数，是由限制的。<br>线程池的大小可以通过 UV_THREADPOOL_SIZE 这个环境变量来改变 或者在nodejs代码中通过 process.env.UV_THREADPOOL_SIZE来重新设置。</p>
<h3 id="问题四：nodejs事件驱动是如何实现的？和浏览器的event-loop是一回事吗？"><a href="#问题四：nodejs事件驱动是如何实现的？和浏览器的event-loop是一回事吗？" class="headerlink" title="问题四：nodejs事件驱动是如何实现的？和浏览器的event loop是一回事吗？"></a>问题四：nodejs事件驱动是如何实现的？和浏览器的event loop是一回事吗？</h3><p>event loop是一个执行模型，在不同的地方有不同的实现。浏览器和nodejs基于不同的技术实现了各自的event loop。</p>
<p>简单来说：</p>
<p>nodejs的event是基于libuv，而浏览器的event loop则在html5的规范中明确定义。<br>libuv已经对event loop作出了实现，而html5规范中只是定义了浏览器中event loop的模型，具体实现留给了浏览器厂商。</p>
<p>我们上面提到了libuv接过了js传递过来的 I/O请求，那么何时来处理回调呢？</p>
<p>libuv有一个事件循环(event loop)的机制，来接受和管理回调函数的执行。</p>
<p>event loop是libuv的核心所在，上面我们提到 js 会把回调和任务交给libuv，libuv何时来调用回调就是 event loop 来控制的。event loop 首先会在内部维持多个事件队列（或者叫做观察者 watcher），比如 时间队列、网络队列等等，使用者可以在watcher中注册回调，当事件发生时事件转入pending状态，再下一次循环的时候按顺序取出来执行，而libuv会执行一个相当于 while true的无限循环，不断的检查各个watcher上面是否有需要处理的pending状态事件，如果有则按顺序去触发队列里面保存的事件，同时由于libuv的事件循环每次只会执行一个回调，从而避免了 竞争的发生。Libuv的 event loop执行图：</p>
<p><img src="https://segmentfault.com/img/bVbaNie?w=444&h=461" alt="图片描述"></p>
<p>nodejs的event loop分为6个阶段，每个阶段的作用如下：<br>timers：执行setTimeout() 和 setInterval()中到期的callback。<br>I/O callbacks：上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行<br>idle, prepare：仅内部使用<br>poll：最为重要的阶段，执行I/O callback，在适当的条件下会阻塞在这个阶段<br>check：执行setImmediate的callback<br>close callbacks：执行close事件的callback，例如socket.on(“close”,func)</p>
<p>event loop的每一次循环都需要依次经过上述的阶段。 每个阶段都有自己的callback队列，每当进入某个阶段，都会从所属的队列中取出callback来执行，当队列为空或者被执行callback的数量达到系统的最大数量时，进入下一阶段。这六个阶段都执行完毕称为一轮循环。</p>
<p>附带event loop 源码：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">uv_run</span><span class="params">(<span class="keyword">uv_loop_t</span>* loop, uv_run_mode mode)</span> </span>&#123;     </span><br><span class="line">    <span class="keyword">int</span> timeout;    </span><br><span class="line">    <span class="keyword">int</span> r;     </span><br><span class="line">    <span class="keyword">int</span> ran_pending;        </span><br><span class="line">    <span class="comment">/* 从uv__loop_alive中我们知道event loop继续的条件是以下三者之一：    </span></span><br><span class="line"><span class="comment">     * 1，有活跃的handles（libuv定义handle就是一些long-lived objects，例如tcp server这样）    </span></span><br><span class="line"><span class="comment">     * 2，有活跃的request     </span></span><br><span class="line"><span class="comment">     * 3，loop中的closing_handles    </span></span><br><span class="line"><span class="comment">     */</span>    </span><br><span class="line">    r = uv__loop_alive(loop);     </span><br><span class="line">    <span class="keyword">if</span> (!r)       </span><br><span class="line">        uv__update_time(loop);       </span><br><span class="line">    <span class="keyword">while</span> (r != <span class="number">0</span> &amp;&amp; loop-&gt;stop_flag == <span class="number">0</span>) &#123;       </span><br><span class="line">        uv__update_time(loop);<span class="comment">//更新时间变量，这个变量在uv__run_timers中会用到       </span></span><br><span class="line">        uv__run_timers(loop);<span class="comment">//timers阶段      </span></span><br><span class="line">        ran_pending = uv__run_pending(loop);<span class="comment">//从libuv的文档中可知，这个其实就是I/O callback阶段,ran_pending指示队列是否为空     </span></span><br><span class="line">        uv__run_idle(loop);<span class="comment">//idle阶段       </span></span><br><span class="line">        uv__run_prepare(loop);<span class="comment">//prepare阶段      </span></span><br><span class="line">        timeout = <span class="number">0</span>;        </span><br><span class="line">        <span class="comment">/* 设置poll阶段的超时时间，以下几种情况下超时会被设为0，这意味着此时poll阶段不会被阻塞，在下面的poll阶段我们还会详细讨论这个  </span></span><br><span class="line"><span class="comment">         * 1，stop_flag不为0    </span></span><br><span class="line"><span class="comment">         * 2，没有活跃的handles和request  </span></span><br><span class="line"><span class="comment">         * 3，idle、I/O callback、close阶段的handle队列不为空，否则，设为timer阶段的callback队列中，距离当前时间最近的那个  </span></span><br><span class="line"><span class="comment">         */</span>           </span><br><span class="line">        <span class="keyword">if</span> ((mode == UV_RUN_ONCE &amp;&amp; !ran_pending) || mode == UV_RUN_DEFAULT)        </span><br><span class="line">            timeout = uv_backend_timeout(loop);         </span><br><span class="line">        uv__io_poll(loop, timeout);<span class="comment">//poll阶段      </span></span><br><span class="line">        uv__run_check(loop);<span class="comment">//check阶段  </span></span><br><span class="line">        uv__run_closing_handles(loop);<span class="comment">//close阶段   </span></span><br><span class="line">        <span class="comment">//如果mode == UV_RUN_ONCE（意味着流程继续向前）时，在所有阶段结束后还会检查一次timers，这个的逻辑的原因不太明确     </span></span><br><span class="line">        <span class="keyword">if</span> (mode == UV_RUN_ONCE) &#123;        </span><br><span class="line">            uv__update_time(loop);    </span><br><span class="line">            uv__run_timers(loop);     </span><br><span class="line">        &#125;    </span><br><span class="line">        r = uv__loop_alive(loop);  </span><br><span class="line">        <span class="keyword">if</span> (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)    </span><br><span class="line">            <span class="keyword">break</span>;  </span><br><span class="line">    &#125;       </span><br><span class="line">    <span class="keyword">if</span> (loop-&gt;stop_flag != <span class="number">0</span>)  </span><br><span class="line">        loop-&gt;stop_flag = <span class="number">0</span>;      </span><br><span class="line">    <span class="keyword">return</span> r;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>这里我们再详细了解一下poll阶段：</strong></p>
<p>poll 阶段有两个主要功能：<br>1、执行下限时间已经达到的timers的回调<br>2、处理 poll 队列里的事件。</p>
<p>当event loop进入 poll 阶段，并且 没有设定的timers（there are no timers scheduled），会发生下面两件事之一：</p>
<p>1、如果 poll 队列不空，event loop会遍历队列并同步执行回调，直到队列清空或执行的回调数到达系统上限；</p>
<p>2、如果 poll 队列为空，则发生以下两件事之一：<br>（1）如果代码已经被setImmediate()设定了回调, event loop将结束 poll 阶段进入 check 阶段来执行 check 队列（里的回调）。<br>（2）如果代码没有被setImmediate()设定回调，event loop将阻塞在该阶段等待回调被加入 poll 队列，并立即执行。</p>
<p>但是，当event loop进入 poll 阶段，并且 有设定的timers，一旦 poll 队列为空（poll 阶段空闲状态）：<br>event loop将检查timers,如果有1个或多个timers的下限时间已经到达，event loop将绕回 timers 阶段。</p>
<p><strong>event loop的一个例子讲述：</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);  </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">someAsyncOperation</span> (<span class="params">callback</span>) </span>&#123;  <span class="comment">// 假设这个任务要消耗 95ms  </span></span><br><span class="line">    fs.readFile(<span class="string">'/path/to/file'</span>, callback);</span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">var</span> timeoutScheduled = <span class="built_in">Date</span>.now();</span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;   </span><br><span class="line">    <span class="keyword">var</span> delay = <span class="built_in">Date</span>.now() - timeoutScheduled;  </span><br><span class="line">    <span class="built_in">console</span>.log(delay + <span class="string">"ms have passed since I was scheduled"</span>); </span><br><span class="line">&#125;, <span class="number">100</span>);  <span class="comment">// someAsyncOperation要消耗 95 ms 才能完成 </span></span><br><span class="line">someAsyncOperation(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;    </span><br><span class="line">    <span class="keyword">var</span> startCallback = <span class="built_in">Date</span>.now();    <span class="comment">// 消耗 10ms...  </span></span><br><span class="line">    <span class="keyword">while</span> (<span class="built_in">Date</span>.now() - startCallback &lt; <span class="number">10</span>) &#123;     </span><br><span class="line">        ; <span class="comment">// do nothing  </span></span><br><span class="line">    &#125; </span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>当event loop进入 poll 阶段，它有个空队列（fs.readFile()尚未结束）。所以它会等待剩下的毫秒，直到最近的timer的下限时间到了。当它等了95ms，fs.readFile()首先结束了，然后它的回调被加到 poll的队列并执行——这个回调耗时10ms。之后由于没有其它回调在队列里，所以event loop会查看最近达到的timer的下限时间，然后回到 timers 阶段，执行timer的回调。</p>
<p>所以在示例里，回调被设定 和 回调执行间的间隔是105ms。</p>
<p><strong>到这里我们再总结一下，整个异步IO的流程：</strong></p>
<p><img src="https://segmentfault.com/img/bVbaNjy?w=779&h=584" alt="图片描述"></p>
<h3 id="问题五、nodejs擅长什么？不擅长什么？"><a href="#问题五、nodejs擅长什么？不擅长什么？" class="headerlink" title="问题五、nodejs擅长什么？不擅长什么？"></a>问题五、nodejs擅长什么？不擅长什么？</h3><p>Node.js 通过 libuv 来处理与操作系统的交互，并且因此具备了异步、非阻塞、事件驱动的能力。因此，NodeJS能响应大量的并发请求。所以，NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景。</p>
<p>上面提到，如果是 I/O 任务，Node.js 就把任务交给线程池来异步处理，高效简单，因此 Node.js 适合处理I/O密集型任务。但不是所有的任务都是 I/O 密集型任务，当碰到CPU密集型任务时，即只用CPU计算的操作，比如要对数据加解密(node.bcrypt.js)，数据压缩和解压(node-tar)，这时 Node.js 就会亲自处理，一个一个的计算，前面的任务没有执行完，后面的任务就只能干等着 。我们看如下代码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> start = <span class="built_in">Date</span>.now();<span class="comment">//获取当前时间戳 </span></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;    </span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">Date</span>.now() - start);   </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000000</span>; i++)&#123;</span><br><span class="line">        <span class="comment">//执行长循环 </span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="number">1000</span>); </span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;  </span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">Date</span>.now() - start);</span><br><span class="line">&#125;, <span class="number">2000</span>);</span><br></pre></td></tr></table></figure>

<p>最终我们的打印结果是：（结果可能因为你的机器而不同）<br>1000<br>3738</p>
<p>对于我们期望2秒后执行的setTimeout函数其实经过了3738毫秒之后才执行，换而言之，因为执行了一个很长的for循环，所以我们整个Node.js主线程被阻塞了，如果在我们处理100个用户请求中，其中第一个有需要这样大量的计算，那么其余99个就都会被延迟执行。如果操作系统本身就是单核，那也就算了，但现在大部分服务器都是多 CPU 或多核的，而 Node.js 只有一个 EventLoop，也就是只占用一个 CPU 内核，当 Node.js 被CPU 密集型任务占用，导致其他任务被阻塞时，却还有 CPU 内核处于闲置状态，造成资源浪费。</p>
<p>其实虽然Node.js可以处理数以千记的并发，但是一个Node.js进程在某一时刻其实只是在处理一个请求。</p>
<p>因此，Node.js 并不适合 CPU 密集型任务。</p>
<p>参考文章：<br><a href="https://www.cnblogs.com/chris-oil/p/5339305.html" target="_blank" rel="noopener">https://www.cnblogs.com/chris…</a><br><a href="https://www.cnblogs.com/onepixel/p/7143769.html" target="_blank" rel="noopener">https://www.cnblogs.com/onepi…</a><br><a href="https://blog.csdn.net/scandly_java/article/details/51203535" target="_blank" rel="noopener">https://blog.csdn.net/scandly…</a><br><a href="http://liyangready.github.io/2015/08/14/nodejs真的是单线程吗？/" target="_blank" rel="noopener">http://liyangready.github.io/…</a><br><a href="https://blog.csdn.net/xjtroddy/article/details/51388655" target="_blank" rel="noopener">https://blog.csdn.net/xjtrodd…</a><br><a href="https://blog.csdn.net/sinat_25127047/article/details/54016931" target="_blank" rel="noopener">https://blog.csdn.net/sinat_2…</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/node-async/" data-id="ckpb8zvm5004ddsvl7uyz9q30" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/nodejs/" rel="tag">nodejs</a></li></ul>

    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/linux-wlan/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          在Linux上配置无线网络
        
      </div>
    </a>
  
  
    <a href="/android-studio/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">Android Studio</div>
    </a>
  
</nav>

  
</article>

</section>
        
          <aside id="sidebar">
  
    

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/android/" rel="tag">android</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/data-structure/" rel="tag">data structure</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/" rel="tag">docker</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/" rel="tag">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/go/" rel="tag">go</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/" rel="tag">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/" rel="tag">java</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javascript/" rel="tag">javascript</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/json/" rel="tag">json</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/" rel="tag">linux</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/maven/" rel="tag">maven</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/" rel="tag">mybatis</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/" rel="tag">mysql</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/network/" rel="tag">network</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/" rel="tag">nginx</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/" rel="tag">nodejs</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/searching/" rel="tag">searching</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sorting/" rel="tag">sorting</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sublime-text/" rel="tag">sublime text</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/termux/" rel="tag">termux</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/theia/" rel="tag">theia</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/typora/" rel="tag">typora</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vala/" rel="tag">vala</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vim/" rel="tag">vim</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue/" rel="tag">vue</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zig/" rel="tag">zig</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/" rel="tag">zookeeper</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div class="widget tagcloud">
      <a href="/tags/android/" style="font-size: 10px;">android</a> <a href="/tags/data-structure/" style="font-size: 10px;">data structure</a> <a href="/tags/docker/" style="font-size: 10px;">docker</a> <a href="/tags/git/" style="font-size: 15px;">git</a> <a href="/tags/go/" style="font-size: 10px;">go</a> <a href="/tags/hexo/" style="font-size: 10px;">hexo</a> <a href="/tags/java/" style="font-size: 18.33px;">java</a> <a href="/tags/javascript/" style="font-size: 11.67px;">javascript</a> <a href="/tags/json/" style="font-size: 10px;">json</a> <a href="/tags/linux/" style="font-size: 20px;">linux</a> <a href="/tags/maven/" style="font-size: 10px;">maven</a> <a href="/tags/mybatis/" style="font-size: 10px;">mybatis</a> <a href="/tags/mysql/" style="font-size: 11.67px;">mysql</a> <a href="/tags/network/" style="font-size: 11.67px;">network</a> <a href="/tags/nginx/" style="font-size: 10px;">nginx</a> <a href="/tags/nodejs/" style="font-size: 13.33px;">nodejs</a> <a href="/tags/searching/" style="font-size: 10px;">searching</a> <a href="/tags/sorting/" style="font-size: 16.67px;">sorting</a> <a href="/tags/sublime-text/" style="font-size: 10px;">sublime text</a> <a href="/tags/termux/" style="font-size: 10px;">termux</a> <a href="/tags/theia/" style="font-size: 10px;">theia</a> <a href="/tags/typora/" style="font-size: 10px;">typora</a> <a href="/tags/vala/" style="font-size: 10px;">vala</a> <a href="/tags/vim/" style="font-size: 15px;">vim</a> <a href="/tags/vue/" style="font-size: 11.67px;">vue</a> <a href="/tags/zig/" style="font-size: 10px;">zig</a> <a href="/tags/zookeeper/" style="font-size: 10px;">zookeeper</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/05/">五月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/04/">四月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/03/">三月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/01/">一月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/11/">十一月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/10/">十月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/09/">九月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/08/">八月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">七月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/06/">六月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/05/">五月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">三月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/02/">二月 2020</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/java-hashmap/">java.util.HashMap</a>
          </li>
        
          <li>
            <a href="/jvm/">JVM</a>
          </li>
        
          <li>
            <a href="/java-locks/">Java锁</a>
          </li>
        
          <li>
            <a href="/java-references/">Java引用</a>
          </li>
        
          <li>
            <a href="/gitea/">Gitea</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 Alanisia<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">

  
<script src="/fancybox/jquery.fancybox.pack.js"></script>




<script src="/js/script.js"></script>




  </div>
</body>
</html>